import json
import os
import shutil
import webbrowser
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

from cold_start_analyzer import phase_helper
from cold_start_analyzer.report_utils import (
    render_callstack_table,
    render_hotspot_table,
    render_phase_table,
)


class ComparisonReportGenerator:
    def __init__(self, result_json: str, output_path: str):
        self.result = self._load_json(result_json)
        self.output_path = output_path
        self.trace_a = self.result.get("trace_a", {})
        self.trace_b = self.result.get("trace_b", {})
        self.comparison = self.result.get("comparison", {})
        self.trace_a_label = self._build_trace_label(self.trace_a, "Trace A")
        self.trace_b_label = self._build_trace_label(self.trace_b, "Trace B")

    def _build_trace_label(self, trace: Dict[str, Any], prefix: str) -> str:
        meta = trace.get("metadata", {})
        file_name = Path(meta.get("file") or "").name
        duration = _format_duration(_sum_phase_duration(trace.get("phase_analysis")))
        return f"{prefix}: {file_name}_{duration}"

    def _load_json(self, json_file: str) -> Dict[str, Any]:
        with open(json_file, "r") as file:
            return json.load(file)

    def _render_trace_section(self, trace: Dict[str, Any], title: str) -> str:
        sections = []
        callstack_rows = trace.get("callstack_table")
        if callstack_rows:
            sections.append(render_callstack_table(callstack_rows, title=f"{title} 调用栈"))
        hotspot_rows = trace.get("callstack_hotspots")
        if hotspot_rows:
            sections.append(render_hotspot_table(hotspot_rows, title=f"{title} 热点函数（Wall）"))
        hotspot_self_rows = trace.get("callstack_hotspots_self")
        if hotspot_self_rows:
            sections.append(render_hotspot_table(hotspot_self_rows, title=f"{title} 热点函数（Self）"))
        if sections:
            return "".join(sections)
        phases = trace.get("phase_breakdown") or trace.get("phase_analysis")
        if not phases:
            return ""
        rows = phase_helper.flatten_phase_tree(phases, include_root=False)
        return render_phase_table(rows, title=title)

    def _render_changes_table(self) -> str:
        records = self.comparison.get("changed") or []
        if not records:
            return "<p>无阶段耗时差异。</p>"
        header = f"""
            <tr>
                <th>层级</th>
                <th>阶段名称</th>
                <th>{self.trace_a_label}</th>
                <th>{self.trace_b_label}</th>
                <th>差值(ms)</th>
                <th>差值(%)</th>
            </tr>
        """
        rows_html = []
        for row in records:
            class_a, class_b = _cell_classes(row.get("cost_a_ns"), row.get("cost_b_ns"))
            diff_class = _diff_class(row.get("diff_ns"))
            rows_html.append(
                f"""
                <tr>
                    <td>{row.get("path")}</td>
                    <td>{row.get("name")}</td>
                    <td class="{class_a}">{row.get("cost_a_ms")}</td>
                    <td class="{class_b}">{row.get("cost_b_ms")}</td>
                    <td class="{diff_class}">{row.get("diff_ms")}</td>
                    <td>{row.get("diff_percent")}</td>
                </tr>
                """
            )
        return f"""
            <div class="card">
                <h2>阶段耗时变化</h2>
                <div class="table-scroll">
                    <table>
                        {header}
                        {''.join(rows_html)}
                    </table>
                </div>
            </div>
        """

    def _render_add_remove(self, key: str, title: str) -> str:
        records = self.comparison.get(key) or []
        if not records:
            return ""
        header = """
            <tr>
                <th>层级</th>
                <th>阶段名称</th>
                <th>耗时(ms)</th>
            </tr>
        """
        rows_html = []
        for row in records:
            rows_html.append(
                f"""
                <tr>
                    <td>{row.get("path")}</td>
                    <td>{row.get("name")}</td>
                    <td>{row.get("cost_ms")}</td>
                </tr>
                """
            )
        return f"""
            <div class="card">
                <h3>{title}</h3>
                <div class="table-scroll">
                    <table>
                        {header}
                        {''.join(rows_html)}
                    </table>
                </div>
            </div>
        """

    def _render_diff_table(self, key: str, title: str) -> str:
        rows = self.comparison.get(key) or []
        if not rows:
            return ""
        header = f"""
            <tr>
                <th>名称</th>
                <th>{self.trace_a_label}</th>
                <th>{self.trace_b_label}</th>
                <th>差值(ms)</th>
            </tr>
        """
        body = []
        for row in rows:
            class_a, class_b = _cell_classes(row.get("cost_a_raw"), row.get("cost_b_raw"))
            diff_class = _diff_class(row.get("diff_raw"))
            body.append(
                f"""
                <tr>
                    <td>{row.get("name")}</td>
                    <td class="{class_a}">{row.get("cost_a_ms")}</td>
                    <td class="{class_b}">{row.get("cost_b_ms")}</td>
                    <td class="{diff_class}">{row.get("diff_ms")}</td>
                </tr>
                """
            )
        return f"""
            <div class="card">
                <h2>{title}</h2>
                <div class="table-scroll">
                    <table>
                        {header}
                        {''.join(body)}
                    </table>
                </div>
            </div>
        """

    def generate(self) -> None:
        added_section = self._render_add_remove("added", "新增阶段（仅 Trace A）")
        removed_section = self._render_add_remove("removed", "缺失阶段（仅 Trace B）")
        callstack_diff = self._render_diff_table("callstack_table_diff", "调用栈耗时变化")
        hotspot_wall_diff = self._render_diff_table("hotspots_wall_diff", "热点函数（Wall）变化")
        hotspot_self_diff = self._render_diff_table("hotspots_self_diff", "热点函数（Self）变化")
        changes_section = self._render_changes_table()

        meta_a = self.trace_a.get("metadata", {})
        meta_b = self.trace_b.get("metadata", {})
        cost_a = self.trace_a.get("phase_analysis", {}).get("cost")
        cost_b = self.trace_b.get("phase_analysis", {}).get("cost")
        trace_a_label = f"Trace A: {meta_a.get('file')} ({_format_duration(cost_a)})"
        trace_b_label = f"Trace B: {meta_b.get('file')} ({_format_duration(cost_b)})"
        html_template = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="utf-8">
            <title>冷启动阶段对比报告</title>
            <style>
                body {{
                    font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
                    margin: 0;
                    padding: 0;
                    background-color: #f5f5f5;
                    color: #333;
                }}
                .container {{
                    max-width: 1200px;
                    margin: 0 auto;
                    padding: 20px;
                }}
                .card {{
                    background-color: white;
                    border-radius: 8px;
                    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
                    margin-bottom: 30px;
                    padding: 20px;
                }}
                h1, h2, h3 {{
                    margin-top: 0;
                }}
                table {{
                    width: 100%;
                    border-collapse: collapse;
                    table-layout: auto;
                }}
                th, td {{
                    padding: 10px;
                    border-bottom: 1px solid #eee;
                    text-align: left;
                }}
                tr:hover {{
                    background-color: #f5f5f5;
                }}
                .phase-table-wrapper {{
                    margin-top: 10px;
                }}
                .table-scroll {{
                    overflow-x: auto;
                }}
                .phase-name {{
                    min-width: 220px;
                }}
                .phase-more {{
                    color: #999;
                    margin-left: 4px;
                    font-size: 12px;
                }}
                .phase-other-tag {{
                    margin-left: 4px;
                    font-size: 12px;
                    color: #666;
                }}
                .better {{ background-color: #e6f4ea; }}
                .worse {{ background-color: #fdecea; }}
                .grid {{
                    display: grid;
                    gap: 20px;
                    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
                }}
            </style>
        </head>
        <body>
            <div class="container">
                <h1>冷启动阶段对比报告</h1>
                <div class="card">
                    <h2>Trace 信息</h2>
                    <p>{trace_a_label} (线程 {meta_a.get('thread')})</p>
                    <p>{trace_b_label} (线程 {meta_b.get('thread')})</p>
                </div>
                {changes_section}
                {hotspot_self_diff}
                {hotspot_wall_diff}
                {callstack_diff}
                <div class="grid">
                    {added_section}
                    {removed_section}
                </div>
            </div>
        </body>
        </html>
        """
        output_file = os.path.join(self.output_path, "comparison_report.html")
        with open(output_file, "w", encoding="utf-8") as file:
            file.write(html_template)
        resources_path = os.path.join(os.path.dirname(__file__), "resources")
        if os.path.exists(resources_path):
            resources_output = os.path.join(self.output_path, "resources")
            os.makedirs(resources_output, exist_ok=True)
            for resource in os.listdir(resources_path):
                shutil.copy(os.path.join(resources_path, resource), os.path.join(resources_output, resource))
        webbrowser.open(output_file)
def _format_duration(cost: Optional[float]) -> str:
    if isinstance(cost, (int, float)):
        return f"{cost / 1_000_000:.2f} ms"
    return "N/A"


def _sum_phase_duration(phase_tree: Optional[Dict[str, Any]]) -> Optional[float]:
    if not isinstance(phase_tree, dict):
        return None
    total = 0.0
    for idx, node in enumerate(phase_tree.get("subs") or []):
        if idx >= 4:
            break
        cost = node.get("cost")
        if isinstance(cost, (int, float)):
            total += cost
    return total if total else None


def _cell_classes(val_a: Optional[float], val_b: Optional[float]) -> Tuple[str, str]:
    if isinstance(val_a, (int, float)) and isinstance(val_b, (int, float)):
        if val_a < val_b:
            return "better", "worse"
        if val_a > val_b:
            return "worse", "better"
    return "", ""


def _diff_class(diff: Optional[float]) -> str:
    if isinstance(diff, (int, float)):
        if diff < 0:
            return "better"
        if diff > 0:
            return "worse"
    return ""
